﻿using System;
using System.Collections.Generic;
using System.Linq;
using Aga.Controls.Tree;
using System.IO;
using System.Drawing;
using System.ComponentModel;
using System.Threading;

namespace MyLibrary.UI
{
    public class FolderBrowserModel : ITreeModel
    {
        private BackgroundWorker _worker;
        private List<BaseItem> _itemsToRead;
        private readonly Dictionary<string, List<BaseItem>> _cache = 
            new Dictionary<string, List<BaseItem>>();

        public FolderBrowserModel()
        {
            _itemsToRead = new List<BaseItem>();

            _worker = new BackgroundWorker {WorkerReportsProgress = true};
            _worker.DoWork += ReadFilesProperties;
            _worker.ProgressChanged += ProgressChanged;
        }

        void ReadFilesProperties(object sender, DoWorkEventArgs e)
        {
            while (_itemsToRead.Count > 0)
            {
                var item = _itemsToRead[0];
                _itemsToRead.RemoveAt(0);

                Thread.Sleep(50); //emulate time consuming operation
                
                if (item is FolderItem)
                {
                    item.Date = (new DirectoryInfo(item.ItemPath)).CreationTime;
                }
                else if (item is FileItem)
                {
                    var info = new FileInfo(item.ItemPath);
                    item.Size = info.Length;
                    item.Date = info.CreationTime;

                    switch (info.Extension.ToLower())
                    {
                        case ".ico":
                            item.Icon = (new Icon(item.ItemPath)).ToBitmap();
                            break;
                        case ".bmp":
                            item.Icon = new Bitmap(item.ItemPath);
                            break;
                    }
                }

                _worker.ReportProgress(0, item);
            }
        }

        void ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            OnNodesChanged(e.UserState as BaseItem);
        }

        private TreePath GetPath(BaseItem item)
        {
            if (item == null)
                return TreePath.Empty;
            
            var stack = new Stack<object>();
            while (item != null)
            {
                stack.Push(item);
                item = item.Parent;
            }
            
            return new TreePath(stack.ToArray());
        }

        public System.Collections.IEnumerable GetChildren(TreePath treePath)
        {
            List<BaseItem> items = null;
            if (treePath.IsEmpty())
            {
                if (_cache.ContainsKey("ROOT"))
                    items = _cache["ROOT"];
                else
                {
                    items = new List<BaseItem>();
                    _cache.Add("ROOT", items);

                    items.AddRange(Environment
                        .GetLogicalDrives()
                        .Select(str => new RootItem(str, this)));
                }
            }
            else
            {
                var parent = treePath.LastNode as BaseItem;
                if (parent != null)
                {
                    if (_cache.ContainsKey(parent.ItemPath))
                        items = _cache[parent.ItemPath];
                    else
                    {
                        items = new List<BaseItem>();
                        try
                        {
                            items.AddRange(Directory.GetDirectories(parent.ItemPath)
                                               .Select(str => new FolderItem(str, parent, this)));

                            items.AddRange(Directory.GetFiles(parent.ItemPath)
                                               .Select(str => new FileItem(str, parent, this)));
                        }
                        catch (IOException)
                        {
                            return null;
                        }
                        
                        _cache.Add(parent.ItemPath, items);
                        _itemsToRead.AddRange(items);
                        
                        if (!_worker.IsBusy)
                            _worker.RunWorkerAsync();
                    }
                }
            }
            return items;
        }

        public bool IsLeaf(TreePath treePath)
        {
            return treePath.LastNode is FileItem;
        }

        public event EventHandler<TreeModelEventArgs> NodesChanged;
        internal void OnNodesChanged(BaseItem item)
        {
            if (NodesChanged != null)
                NodesChanged(this, new TreeModelEventArgs(
                    GetPath(item.Parent), new object[] {item}));
        }

        public event EventHandler<TreeModelEventArgs> NodesInserted;
        public event EventHandler<TreeModelEventArgs> NodesRemoved;
        public event EventHandler<TreePathEventArgs> StructureChanged;
        
        public void OnStructureChanged()
        {
            if (StructureChanged != null)
                StructureChanged(this, new TreePathEventArgs());
        }
    }
}
